package com.acme.domain.bank;

import com.acme.domain.account.Account;
import com.acme.domain.bank.listeners.Listener;
import com.acme.domain.client.Client;

import java.io.*;
import java.util.*;

public class Bank implements Serializable {

    private Set<Client> clients = new HashSet<Client>();
    private Set<Listener> listeners = new HashSet<Listener>();

    public final Set<Client> getClients() {
        return Collections.unmodifiableSet(clients);
    }

    //Bank itself decides who can use its services
    public BankService getService() {
        return new RealBankService();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Bank bank = (Bank) o;

        if (clients != null ? !clients.equals(bank.clients) : bank.clients != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return clients != null ? clients.hashCode() : 0;
    }

    @Override
    public String toString() {
        return "Bank{" +
                "clients=" + clients +
                '}';
    }

    //Bank can give services to some sides
    //and Bank is ensured that noone makes it hurt
    //because the only way to deal with it is via its Service.
    private class RealBankService implements com.acme.domain.bank.BankService {

        @Override
        public void addListener(Listener listener) {
            listeners.add(listener);
            for (Client client: clients) {
                listener.onClientAdded(client);
            }
        }

        @Override
        public void addClient(final Client client) {
            Iterator<Client> iterator = clients.iterator();
            //yup, we have Set.contains, but also we need link to that position
            boolean clientFound = false;
            while (iterator.hasNext()) {
                Client observedClient = iterator.next();
                if (observedClient.equals(client)) {
                    clientFound = true;
                    observedClient.addAccounts((Account[])client.getAccounts().toArray());
                    break;
                }
            }
            if (!clientFound) {
                addClient(client);
                for (Listener listener: listeners) {
                    listener.onClientAdded(client);
                }
            }
        }

        @Override
        public void addClientWithAccounts(final Client client, final Account... accounts){
            client.addAccounts(accounts);
            addClient(client);
        }

        @Override
        public void saveBank(final String path) {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {
                oos.writeObject(Bank.this);
            } catch (IOException e) {
                System.err.println(e);
            }
        }

        //i think this should rewrite current instance of bank
        //as we sync with WiseDataBase.
        //if the thing is important its in DataBase
        @Override
        public void readBank(final String path) {
            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
                clients.clear();
                clients.addAll(((Bank) ois.readObject()).clients);
            } catch (ClassNotFoundException | IOException | ClassCastException e) {
                System.err.println(e);
            }
            return;
        }
    }
}
